// use std::debug::Debug;
use std::ops::Add;

fn add<T: Add<T, Output = T>>(a: T, b: T) -> T {
    a + b
}

#[test]
fn add_test() {
    assert_eq!(201, add(100i32, 101i32));
}

#[test]
fn point_add_test() {
    #[derive(Debug, PartialEq)]
    struct Point {
        x: i32,
        y: i32,
    }

    impl Add for Point {
        type Output = Point;
        fn add(self, rhs: Self) -> Self {
            Point {
                x: self.x + rhs.x,
                y: self.y + rhs.y,
            }
        }
    }

    assert_eq!(
        Point { x: 4, y: 6 },
        Point { x: 1, y: 2 } + Point { x: 3, y: 4 }
    )
}

#[test]
fn generics_point_test() {
    #[derive(Debug, PartialEq)]
    struct Point<T> {
        x: T,
        y: T,
    }

    impl<T: Add<Output = T>> Add for Point<T> {
        type Output = Point<T>;
        fn add(self, rhs: Self) -> Point<T> {
            Point {
                x: self.x + rhs.x,
                y: self.y + rhs.y,
            }
        }
    }

    assert_eq!(
        Point { x: 4, y: 6 },
        Point { x: 1, y: 2 } + Point { x: 3, y: 4 }
    )
}

#[test]
fn test_where_demo() {
    fn foo<T: Clone, K: Clone + std::fmt::Debug>(x: T, y: K) {
        let _x = x.clone();
        let _y = y.clone();
        println!("y is {:?}", y);
    }

    fn bar<T, K>(x: T, y: K)
    where
        T: Clone,
        K: Clone + std::fmt::Debug,
    {
        let _x = x.clone();
        let _y = y.clone();
        println!("y is {:?}", y);
    }

    foo(1, 2);
    bar(1, 2);
}
